package top.healthylife.gateway.core.controller;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSONObject;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import top.healthylife.basics.utils.exception.BusinessException;
import top.healthylife.basics.utils.utils.R;
import top.healthylife.basics.utils.utils.SystemThreadLocal;
import top.healthylife.gateway.core.config.BaseConfig;
import top.healthylife.gateway.core.config.SystemInit;
import top.healthylife.gateway.core.service.CheckSecretService;
import top.healthylife.gateway.core.service.ParentFace;
import top.healthylife.gateway.dto.request.req.ApiReqDto;
import top.healthylife.gateway.dto.request.result.ApiResultDto;
import top.heathylife.gateway.common.cons.SystemCons;
import top.heathylife.gateway.common.utils.SecretJsonUtils;

import javax.annotation.Resource;
import java.net.URLDecoder;
import java.util.Map;


/**
 * 统一的Controller，每一个子项目都使用这同一个
 *
 * @author maoxiaomeng
 */
@Api(tags = "当前服务所有API统一入口")
@Slf4j
@RestController
@RequestMapping("/api/unify")
@SuppressWarnings("all")
public class UnifyApi {

    @Resource
    CheckSecretService checkSecretService;


    @SneakyThrows
    @PostMapping
    @ApiOperation(value = "统一接口服务调用", notes = "请求使用的凭证\n有效期，给一个具体的时间秒数，如果在此秒数内查询过的则调用缓存查询,如果为0或-1，直接调用真实接口,默认90秒时间")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "apiName", paramType = "header", value = "接口名称"),
            @ApiImplicitParam(name = "secret", paramType = "header", value = "请求使用的凭证"),
            @ApiImplicitParam(name = "account", paramType = "header", value = "凭证的账号信息"),
            @ApiImplicitParam(name = "no", paramType = "header", value = "流水编号，数值类型"),
            @ApiImplicitParam(name = "data", paramType = "body", value = "业务数据Map对象数据"),
    })
    public R<ApiResultDto> execute(@RequestBody Map<String, Object> data, @RequestHeader String apiName, @RequestHeader("secret") String secret, @RequestHeader String account, @RequestHeader("no") String no) {
        apiName = URLDecoder.decode(apiName, "utf-8");
        //如果为-1，直接调用真实接口,
        //如果为0则必定走缓存,
        //如果>0,则根据实际时间间隔,在此秒数内查询过的则调用真实接口
        //默认90秒时间
        //加密,请求参数，加上接口名称，防止不同接口名称，相同请求参数的访问问题
        //调用真实接口
        SystemThreadLocal.set(SystemCons.NO, no);
        log.info("请求接口方法{},编号为{},secret:{}", apiName, no, secret);
        //设置请求的接口类型操作
        ParentFace parentFace = SystemInit.getParentFace(apiName);
        //根据名称获取入参泛型
        Class<? extends ApiReqDto> reqDto = SystemInit.getReqDto(apiName);
        //设置请求参数
        SystemInit.setSystemConfig(account, SystemInit.getSystemClass());
        //传递本地请求秘钥
        SystemThreadLocal.set(SystemCons.secret, secret);
        //检查参数是否可用，并执行接口方法
        ApiReqDto apiReqDto;
        try {
            apiReqDto = BeanUtil.mapToBean(data, reqDto, true);
        } catch (Exception e) {
            return R.failed("数据类型不正确,请重新输入");
        }
        ApiResultDto obj = new ApiResultDto();
        try {
            obj = (ApiResultDto) parentFace.checkAndExecute(apiReqDto);
        } catch (Exception e) {
            if (e.getMessage() == null) {
                return R.failed(obj, "null exception");
            }
            return R.failed(obj, e.getMessage());
        } finally {
            obj.setReqMetadata(SystemThreadLocal.get(SystemCons.REQ_ORIGIN_DATA));
            obj.setMetadata(SystemThreadLocal.get(SystemCons.ORIGIN_DATA));
            obj.setCls(obj.getClass().getName());
            obj.setNo(no);
            obj.setHitCache(false);
            SystemThreadLocal.remove(SystemCons.ORIGIN_DATA);
            SystemThreadLocal.remove(SystemCons.REQ_ORIGIN_DATA);
        }
        //设置返回类型API Gateway网关层需要使用
        return R.ok(obj);
    }

    @SneakyThrows
    @PostMapping("/check")
    @ApiOperation(value = "测试凭证是否可用", notes = "测试凭证是否可用")
    public R<Boolean> check(@RequestHeader("secret") String secret, @RequestHeader String account) {
        String message;
        try {
            //设置请求参数
            account = SecretJsonUtils.decodeBase64(account);
            log.debug("测试凭证是否可用, account: {}", account);
            Object object = JSONObject.parseObject(account, SystemInit.getSystemClass());
            BaseConfig baseConfig = (BaseConfig) object;
            checkSecretService.checkSecret(baseConfig);
        } catch (BusinessException e) {
            message = e.getMessage();
            return R.failed(false, message);
        }
        return R.ok(true);
    }

}
